﻿/*****************************************************************************

   Project:   WP7AppLifecycleService
   File:      WP7AppLifecycleService.cs 
   Creator:   David Carballo (dacarb@gmail.com)
   
   Description:
      Definition of WP7AppLifecycleService interface.

   (C) Copyright 2011, David Carballo
       http://wp7applifecycle.codeplex.com/license


******************************************************************************/

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;

namespace WP7AppLifecycleService
{
    /// <summary>
    /// Main class of the library, which implements the facade interface <see cref="IWP7AppLifecycleService"/>
    /// and the internal <see cref="IApplicationService"/> which turns the class into a service that can be
    /// instantiated and added to ApplicationLifetimeObjects.
    /// </summary>
    public sealed class WP7AppLifecycleApplicationService : IWP7AppLifecycleService, IApplicationService
    {
        #region Fields
        
        /// <summary>
        /// Constant with the name of the RootFrame property the class monitors changes for.
        /// </summary>
        private readonly string RootFramePropertyNameConstant = "RootFrame";

        /// <summary>
        /// Constant with the message when throwing an exception because the application class
        /// does not implement <see cref="IWP7ApplicationInterfaceNotImplementedMessage"/>.
        /// </summary>
        private readonly string IWP7ApplicationInterfaceNotImplementedMessage = "Interface IWP7Application is not implemented by the application object";

        /// <summary>
        /// static instance for the singleton as per the singleton pattern.
        /// </summary>
        private static WP7AppLifecycleApplicationService current;

        /// <summary>
        /// stored to unsubscribe to events in case it changes.
        /// </summary>
        private PhoneApplicationFrame previousRootFrame = null;

        /// <summary>
        /// Private dictionary member to store and relate instances of the internal <see cref="WP7AppLifecycleServiceItem"/> class
        /// with instances of <see cref="PhoneApplicationPage"/>.
        /// </summary>
        private Dictionary<PhoneApplicationPage, WP7AppLifecycleServiceItem> registeredItems;

        /// <summary>
        /// Private member to store the list of <see cref="IPhoneLockedHandler"/> instances registered
        /// to handle phone lock/unlock events.
        /// </summary>
        private List<IPhoneLockedHandler> registeredPhoneLockHandlers;

        #endregion Fields

        #region Constructors

        /// <summary>
        /// Prevents a default instance of the <see cref="WP7AppLifecycleApplicationService"/> class from being created.
        /// </summary>
        /// <remarks>private as per the singleton pattern.</remarks>
        private WP7AppLifecycleApplicationService()
        {
            this.previousRootFrame = null;
            this.registeredItems = new Dictionary<PhoneApplicationPage, WP7AppLifecycleServiceItem>();
            this.registeredPhoneLockHandlers = new List<IPhoneLockedHandler>();
        }

        #endregion Constructors

        #region Events

        /// <summary>
        /// This event is raised when an application restart is required in order
        /// to for the idle detection mode to be active again after it has been
        /// disabled in the application (in order to get Locked/Unlocked) notifications.
        /// </summary>
        public event EventHandler RestartRequiredAfterPhoneLockedHandlerUnregistered;

        #endregion Events

        #region Properties

        /// <summary>
        /// Gets the singleton instance of <see cref="WP7AppLifecycleApplicationService"/>.
        /// </summary>
        public static WP7AppLifecycleApplicationService Current
        {
            get
            {
                if (null == current)
                {
                    current = new WP7AppLifecycleApplicationService();
                }

                System.Diagnostics.Debug.Assert(current != null, "It was impossible to create a new instance of WP7AppLifecycleApplicationService");

                return current;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the application is currently running in a locked phone
        /// </summary>
        public bool IsRunningUnderLock
        {
            get;
            private set;
        }

        #endregion Properties

        #region interface IApplicationService

        /// <summary>
        /// Called by an application in order to initialize the application extension service.
        /// </summary>
        /// <param name="context">Provides information about the application state.</param>
        public void StartService(ApplicationServiceContext context)
        {
            if (Application.Current != null)
            {
                IWP7Application app = Application.Current as IWP7Application;
                if (null == app)
                {
                    throw new NotImplementedException(this.IWP7ApplicationInterfaceNotImplementedMessage);
                }

                app.PropertyChanged += this.ApplicationPropertyChanged;
                this.UnsubscribeFromRootFrameEvents();
                this.SubscribeToRootFrameEvents(app);
            }

            PhoneApplicationService.Current.Activated += this.ApplicationActivated;
            PhoneApplicationService.Current.Deactivated += this.ApplicationDeactivated;
        }

        /// <summary>
        /// Called by an application in order to stop the application extension service. 
        /// </summary>
        public void StopService()
        {
            if (Application.Current != null)
            {
                IWP7Application app = Application.Current as IWP7Application;
                if (null == app)
                {
                    throw new NotImplementedException(this.IWP7ApplicationInterfaceNotImplementedMessage);
                }

                this.UnsubscribeFromRootFrameEvents();
                app.PropertyChanged -= this.ApplicationPropertyChanged;
            }

            PhoneApplicationService.Current.Activated -= this.ApplicationActivated;
            PhoneApplicationService.Current.Deactivated -= this.ApplicationDeactivated;
        }

        #endregion interface IApplicationService

        #region public methods interface IWP7AppLifecycleService

        /// <summary>
        /// Registers an <see cref="IApplicationStateHandler"/> instance, associating it to a specific 
        /// <see cref="Microsoft.Phone.Controls.PhoneApplicationPage"/> instance.
        /// </summary>
        /// <param name="page">The <see cref="Microsoft.Phone.Controls.PhoneApplicationPage"/> instance to associate the handler to.</param>
        /// <param name="handler">The <see cref="IApplicationStateHandler"/> instance that handles
        /// application state for the page.</param>
        /// <exception cref="System.ArgumentNullException">Either page or handler is a null reference (Nothing in Visual Basic).</exception>
        public void RegisterApplicationStateHandler(PhoneApplicationPage page, IApplicationStateHandler handler)
        {
            ParameterInfo[] parameters = System.Reflection.MethodInfo.GetCurrentMethod().GetParameters();

            if (null == page)
            {
                throw new ArgumentNullException(parameters[0].Name);
            }

            if (null == handler)
            {
                throw new ArgumentNullException(parameters[1].Name);
            }

            if (!this.registeredItems.ContainsKey(page))
            {
                WP7AppLifecycleServiceItem serviceItem = new WP7AppLifecycleServiceItem()
                {
                    Page = page,
                    ApplicationStateHandler = handler,
                    WasItTombstoned = PhoneApplicationService.Current.StartupMode == StartupMode.Activate
                };

                this.registeredItems[page] = serviceItem;
            }
            else
            {
                this.registeredItems[page].ApplicationStateHandler = handler;
            }
        }

        /// <summary>
        /// Registers an <see cref="IPageStateHandler"/> instance, associating it to a specific 
        /// <see cref="Microsoft.Phone.Controls.PhoneApplicationPage"/> instance.
        /// </summary>
        /// <param name="page">The <see cref="Microsoft.Phone.Controls.PhoneApplicationPage"/> instance to associate the handler to.</param>
        /// <param name="handler">The <see cref="IPageStateHandler"/> instance that handles
        /// page state for the page.</param>
        /// <exception cref="System.ArgumentNullException">Either page or handler is a null reference (Nothing in Visual Basic).</exception>
        public void RegisterPageStateHandler(PhoneApplicationPage page, IPageStateHandler handler)
        {
            ParameterInfo[] parameters = System.Reflection.MethodInfo.GetCurrentMethod().GetParameters();

            if (null == page)
            {
                throw new ArgumentNullException(parameters[0].Name);
            }

            if (null == handler)
            {
                throw new ArgumentNullException(parameters[1].Name);
            }

            if (!this.registeredItems.ContainsKey(page))
            {
                WP7AppLifecycleServiceItem serviceItem = new WP7AppLifecycleServiceItem()
                {
                    Page = page,
                    PageStateHandler = handler,
                    WasItTombstoned = PhoneApplicationService.Current.StartupMode == StartupMode.Activate
                };

                this.registeredItems[page] = serviceItem;
            }
            else
            {
                this.registeredItems[page].PageStateHandler = handler;
            }
        }

        /// <summary>
        /// Utility method provided that tells whether a given <see cref="Microsoft.Phone.Controls.PhoneApplicationPage"/> instance
        /// was tombstoned or not in case the application needs or wants to perform additional work.
        /// </summary>
        /// <param name="page">The <see cref="Microsoft.Phone.Controls.PhoneApplicationPage"/> instance to associate the handler to.</param>
        /// <returns>A boolean value indicating whether the given instance was tombstoned or not.</returns>
        /// <exception cref="System.ArgumentNullException">The page is a null reference (Nothing in Visual Basic).</exception>
        public bool WasItTombstoned(Microsoft.Phone.Controls.PhoneApplicationPage page)
        {
            ParameterInfo[] parameters = System.Reflection.MethodInfo.GetCurrentMethod().GetParameters();

            if (null == page)
            {
                throw new ArgumentNullException(parameters[0].Name);
            }

            bool wasItTombstoned = false;

            if (this.registeredItems.ContainsKey(page))
            {
                wasItTombstoned = this.registeredItems[page].WasItTombstoned;
            }

            return wasItTombstoned;
        }

        /// <summary>
        /// Registers an <see cref="IPhoneLockedHandler"/> instance.
        /// </summary>
        /// <param name="handler">The <see cref="IPhoneLockedHandler"/> instance that handles
        /// when the phone is locked/unlocked.</param>
        /// <exception cref="System.ArgumentNullException">The handler is a null reference (Nothing in Visual Basic).</exception>
        public void RegisterPhoneLockedHandler(IPhoneLockedHandler handler)
        {
            ParameterInfo[] parameters = System.Reflection.MethodInfo.GetCurrentMethod().GetParameters();

            if (null == handler)
            { 
                throw new ArgumentNullException(parameters[0].Name); 
            }

            if (!this.registeredPhoneLockHandlers.Contains(handler))
            {
                this.registeredPhoneLockHandlers.Add(handler);
                DisableApplicationIdleDetectionMode();
                this.SubscribeToRootFrameLockEvents();
            }
        }

        /// <summary>
        /// Unregisters an <see cref="IPhoneLockedHandler"/> instance.
        /// </summary>
        /// <param name="handler">The <see cref="IPhoneLockedHandler"/> instance that handles
        /// when the phone is locked/unlocked.</param>
        /// <exception cref="System.ArgumentNullException">The handler is a null reference (Nothing in Visual Basic).</exception>
        public void UnregisterPhoneLockedHandler(IPhoneLockedHandler handler)
        {
            ParameterInfo[] parameters = System.Reflection.MethodInfo.GetCurrentMethod().GetParameters();

            if (null == handler)
            {
                throw new ArgumentNullException(parameters[0].Name);
            }

            if (this.registeredPhoneLockHandlers.Contains(handler))
            {
                this.registeredPhoneLockHandlers.Remove(handler);

                if (0 == this.registeredPhoneLockHandlers.Count)
                {
                    this.UnsubscribeFromRootFrameLockEvents();
                    this.EnableApplicationIdleDetection();
                }
            }
        }

        #endregion public methods interface IWP7AppLifecycleService

        #region private static methods

        /// <summary>
        /// Sets the ApplicationIdleDetectionMode to Disabled in order to get
        /// Obscured and Unobscured events for the <see cref="PhoneApplicationFrame"/> instance
        /// that is the actual root frame.
        /// </summary>
        private static void DisableApplicationIdleDetectionMode()
        {
            PhoneApplicationService.Current.ApplicationIdleDetectionMode = IdleDetectionMode.Disabled;
        }

        /// <summary>
        /// Notifies a specific <see cref="IPhoneLockedHandler"/> instance that
        /// the phone has been locked (the shell chrome is covering the frame).
        /// </summary>
        /// <param name="plh">Specific  <see cref="IPhoneLockedHandler"/> instance to be notified.</param>
        private static void NotifyLock(IPhoneLockedHandler plh)
        {
            try
            {
                plh.Locked();
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("\tLock threw exception {0}", ex.ToString());
            }
        }

        /// <summary>
        /// Notifies a specific <see cref="IPhoneLockedHandler"/> instance that
        /// the phone has been unlocked (the shell chrome is no longer covering the frame).
        /// </summary>
        /// <param name="plh">Specific  <see cref="IPhoneLockedHandler"/> instance to be notified.</param>
        private static void NotifyUnlock(IPhoneLockedHandler plh)
        {
            try
            {
                plh.Unlocked();
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("\tLock threw exception {0}", ex.ToString());
            }
        }

        /// <summary>
        /// Determines whether application state should be restored for a give page (represented
        /// via the <see cref="WP7AppLifecycleServiceItem"/> instance.
        /// </summary>
        /// <param name="item">A <see cref="WP7AppLifecycleServiceItem"/> instance</param>
        /// <returns>A boolean value indicating whether application state should be retored</returns>
        private static bool ShouldRestoreApplicationState(WP7AppLifecycleServiceItem item)
        {
            bool shouldRestoreApplicationState = false;

            try
            {
                if (null != item.ApplicationStateHandler)
                {
                    shouldRestoreApplicationState = item.ApplicationStateHandler.IsThereApplicationStateSaved;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("\tIsThereApplicationStateSaved threw exception {0}", ex.ToString());
            }

            return shouldRestoreApplicationState;
        }

        /// <summary>
        /// Restores application state for a given page (represented
        /// via the <see cref="WP7AppLifecycleServiceItem"/> instance.
        /// </summary>
        /// <param name="item">A <see cref="WP7AppLifecycleServiceItem"/> instance.</param>
        private static void RestoreApplicationState(WP7AppLifecycleServiceItem item)
        {
            if (null != item.ApplicationStateHandler)
            {
                try
                {
                    item.ApplicationStateHandler.RestoreApplicationState();
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine("\tRestoreApplicationState threw exception {0}", ex.ToString());
                }
                finally
                {
                    try
                    {
                        item.ApplicationStateHandler.DeleteApplicationState();
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine("\tDeleteApplicationState threw exception {0}", ex.ToString());
                    }
                }
            }
        }

        /// <summary>
        /// Deletes any existing application state for a given page (represented
        /// via the <see cref="WP7AppLifecycleServiceItem"/> instance.
        /// </summary>
        /// <param name="item">A <see cref="WP7AppLifecycleServiceItem"/> instance.</param>
        private static void DeleteApplicationState(WP7AppLifecycleServiceItem item)
        {
            if (null != item.ApplicationStateHandler)
            {
                try
                {
                    item.ApplicationStateHandler.DeleteApplicationState();
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine("\tDeleteApplicationState threw exception {0}", ex.ToString());
                }
            }
        }

        /// <summary>
        /// Restores the page state for a given page (represented
        /// via the <see cref="WP7AppLifecycleServiceItem"/> instance.
        /// </summary>
        /// <param name="item">A <see cref="WP7AppLifecycleServiceItem"/> instance.</param>
        private static void RestorePageState(WP7AppLifecycleServiceItem item)
        {
            if (null != item.PageStateHandler)
            {
                try
                {
                    item.PageStateHandler.RestorePageState(PhoneApplicationService.Current.State);
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine("\tRestorePageState threw exception {0}", ex.ToString());
                }
                finally
                {
                    try
                    {
                        item.PageStateHandler.DeletePageState(PhoneApplicationService.Current.State);
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine("\tDeletePageState threw exception {0}", ex.ToString());
                    }
                }
            }
        }

        #endregion private static methods
        
        #region private methods

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        /// <param name="sender">reference to the object that raised the event.</param>
        /// <param name="e"><see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance with the specific arguments 
        /// for the PropertyChanged event.</param>
        private void ApplicationPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == this.RootFramePropertyNameConstant)
            {
                IWP7Application app = Application.Current as IWP7Application;
                if (null == app)
                {
                    throw new NotImplementedException(this.IWP7ApplicationInterfaceNotImplementedMessage);
                }

                this.UnsubscribeFromRootFrameEvents();
                this.SubscribeToRootFrameEvents(app);
            }
        }

        /// <summary>
        /// Occurs when a new navigation is requested.
        /// </summary>
        /// <param name="sender">reference to the object that raised the event.</param>
        /// <param name="e"><see cref="System.Windows.Navigation.NavigatingCancelEventArgs"/> instance with the specific arguments 
        /// for the Navigating event.</param>
        private void NavigationServiceNavigating(object sender, System.Windows.Navigation.NavigatingCancelEventArgs e)
        {
            NavigationService navigatingService = sender as NavigationService;

            if (e.Uri.IsAbsoluteUri)
            {   // i.e. app://external
                foreach (WP7AppLifecycleServiceItem item in this.registeredItems.Values)
                {
                    if (null != item.PageStateHandler)
                    {
                        try
                        {
                            item.PageStateHandler.SavePageState(PhoneApplicationService.Current.State);
                        }
                        catch (Exception ex)
                        {
                            System.Diagnostics.Debug.WriteLine("\tSavePageState threw exception {0}", ex.ToString());
                        }
                    }
                }
            }
            else
            {
                if (e.NavigationMode == NavigationMode.Back)
                {
                    foreach (WP7AppLifecycleServiceItem item in this.registeredItems.Values)
                    {
                        if (item.PageUri == navigatingService.CurrentSource)
                        {
                            this.registeredItems.Remove(item.Page);
                            break;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Occurs when the content that is being navigated to has been found, and is available from the Content property, 
        /// although it may not have completed loading. 
        /// </summary>
        /// <param name="sender">reference to the object that raised the event.</param>
        /// <param name="e"><see cref="System.Windows.Navigation.NavigationEventArgs"/> instance with the specific arguments 
        /// for the Navigated event.</param>
        private void NavigationServiceNavigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
        {            
            if (null != e.Content)
            {
                PhoneApplicationPage page = e.Content as PhoneApplicationPage;

                if (this.registeredItems.ContainsKey(page))
                {
                    WP7AppLifecycleServiceItem item = this.registeredItems[page];

                    if (null != item)
                    {
                        item.PageUri = e.Uri;

                        if (item.WasItTombstoned)
                        {
                            item.WasItTombstoned = false;
                            if (ShouldRestoreApplicationState(item))
                            {
                                RestoreApplicationState(item);
                            }

                            RestorePageState(item);
                        }
                        else
                        {
                            if (ShouldRestoreApplicationState(item))
                            {
                                DeleteApplicationState(item);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Occurs when the application is being made active after previously being tombstoned. 
        /// </summary>
        /// <param name="sender">reference to the object that raised the event.</param>
        /// <param name="e"><see cref="ActivatedEventArgs"/> instance with the specific arguments for the ApplicationActivated event.</param>
        /// <remarks>The service does nothing to restore application data, since it will do so 
        /// when navigation to pages actually happens.</remarks>
        private void ApplicationActivated(object sender, ActivatedEventArgs e)
        {
        }

        /// <summary>
        /// Occurs when the application is being deactivated and tombstoned. 
        /// </summary>
        /// <param name="sender">reference to the object that raised the event.</param>
        /// <param name="e"><see cref="DeactivatedEventArgs"/> instance with the specific arguments for the ApplicationDeactivated event.</param>
        private void ApplicationDeactivated(object sender, DeactivatedEventArgs e)
        {
            foreach (WP7AppLifecycleServiceItem handler in this.registeredItems.Values)
            {
                if (null != handler.ApplicationStateHandler)
                {
                    try
                    {
                        handler.ApplicationStateHandler.SaveApplicationState();
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine("\tSaveApplicationState threw exception {0}", ex.ToString());
                    }
                }
            }
        }

        /// <summary>
        /// Occurs when the shell chrome is covering the frame
        /// </summary>
        /// <param name="sender">reference to the object that raised the event.</param>
        /// <param name="e"><see cref="ObscuredEventArgs"/> instance with the specific arguments for the Obscured event.</param>
        private void RootFrameObscured(object sender, ObscuredEventArgs e)
        {
            this.IsRunningUnderLock = e.IsLocked;
            if (e.IsLocked)
            {
                this.registeredPhoneLockHandlers.ForEach(item => NotifyLock(item));
            }
        }

        /// <summary>
        /// Occurs when the shell chrome is no longer covering the frame
        /// </summary>
        /// <param name="sender">reference to the object that raised the event.</param>
        /// <param name="e"><see cref="EventArgs"/> instance with the specific arguments for the Unobscured event.</param>
        private void RootFrameUnobscured(object sender, EventArgs e)
        {
            this.IsRunningUnderLock = false;
            this.registeredPhoneLockHandlers.ForEach(item => NotifyUnlock(item));
        }

        #endregion private methods

        #region StateHandler-related: level 2 methods

        /// <summary>
        /// Subscribes to the events of interest from RootFrame in order to provide the service
        /// features.
        /// </summary>
        /// <param name="app">the <see cref="IWP7Application"/> instance providing access to
        /// the RootFrame.</param>
        private void SubscribeToRootFrameEvents(IWP7Application app)
        {
            if (null != app.RootFrame)
            {
                this.previousRootFrame = app.RootFrame;
                app.RootFrame.Navigating += this.NavigationServiceNavigating;
                app.RootFrame.Navigated += this.NavigationServiceNavigated;
            }
        }

        /// <summary>
        /// Unsubscribes to the events of interest from RootFrame.
        /// </summary>
        private void UnsubscribeFromRootFrameEvents()
        {
            if (null != this.previousRootFrame)
            {
                this.previousRootFrame.Navigating -= this.NavigationServiceNavigating;
                this.previousRootFrame.Navigated -= this.NavigationServiceNavigated;
            }
        }

        #endregion StateHandler-related: level 2 methods

        #region LockHandler-related: level 2 methods

        /// <summary>
        /// Sets the ApplicationIdleDetectionMode to Enabled again in order to stop getting
        /// Obscured and Unobscured events for the <see cref="PhoneApplicationFrame"/> instance
        /// that is the actual root frame.
        /// </summary>
        /// <remarks>
        /// In the current release of WP7 this will always generate and <see cref="InvalidOperationException"/>
        /// which means it will only take effect after the application is restarted.
        /// </remarks>
        private void EnableApplicationIdleDetection()
        {
            bool isRestartRequired = false;
            try
            {
                PhoneApplicationService.Current.ApplicationIdleDetectionMode = IdleDetectionMode.Enabled;
            }
            catch (InvalidOperationException)
            {
                // This exception is expected in the current release.
                isRestartRequired = true;

                // we can not set it; we have to restart app ...                            
            }

            if (isRestartRequired)
            {
                this.NotifyRestart();
            }
        }

        /// <summary>
        /// Subscribes to the Obscured and Unobscured events of the <see cref="PhoneApplicationFrame"/> instance
        /// that is the actual root frame.
        /// </summary>
        private void SubscribeToRootFrameLockEvents()
        {
            if (null != this.previousRootFrame)
            {
                this.previousRootFrame.Obscured += this.RootFrameObscured;
                this.previousRootFrame.Unobscured += this.RootFrameUnobscured;
            }
        }

        /// <summary>
        /// Unsubscribes to the Obscured and Unobscured events of the <see cref="PhoneApplicationFrame"/> instance
        /// that is the actual root frame.
        /// </summary>
        private void UnsubscribeFromRootFrameLockEvents()
        {
            if (null != this.previousRootFrame)
            {
                this.previousRootFrame.Obscured -= this.RootFrameObscured;
                this.previousRootFrame.Unobscured -= this.RootFrameUnobscured;
            }
        }

        /// <summary>
        /// Notifies applications via the RestartRequiredAfterPhoneLockedHandlerUnregistered event
        /// of <see cref="IWP7AppLifecycleService"/> that an application restart is required in order
        /// to for the idle detection mode to be active again after it has been
        /// disabled in the application (in order to get Locked/Unlocked) notifications.
        /// </summary>
        private void NotifyRestart()
        {
            if (null != this.RestartRequiredAfterPhoneLockedHandlerUnregistered)
            {
                try
                {
                    this.RestartRequiredAfterPhoneLockedHandlerUnregistered(this, new EventArgs());
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine("\tRestartRequiredAfterPhoneLockedHandlerUnregistered threw exception {0}", ex.ToString());
                }
            }
        }

        #endregion LockHandler-related: level 2 methods
    }
}
